﻿using System;
using System.Diagnostics;

using FlickrNet;
using FlickrApi = FlickrNet.Flickr;

using Yorrick.FlickrMetadataSynchr.Data;


namespace Yorrick.FlickrMetadataSynchr.Flickr
{
    internal sealed class FlickrMetadataUpdater
    {
        #region Private constants
        /// <summary>
        /// The minimum date that is supported by Flickr.
        /// </summary>
        private static readonly DateTime _MinimumSupportedFlickrDate = new DateTime(1970, 1, 1);
        #endregion

        #region Private fields
        /// <summary>
        /// The Flickr API object.
        /// </summary>
        private FlickrApi _flickrApi;
        #endregion

        #region Public properties
        /// <summary>
        /// Gets or sets whether or not to simulate updating metadata.
        /// </summary>
        public bool SimulateUpdatingMetadata
        {
            get; set;
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Constructs a new instance.
        /// </summary>
        /// <param name="flickrApi">The Flickr API object to use.</param>
        public FlickrMetadataUpdater(FlickrApi flickrApi)
        {
            _flickrApi = flickrApi;
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Updates the metadata for a picture on Flickr.
        /// </summary>
        /// <param name="metadata">The metadata.</param>
        /// <exception cref="FlickrException">When an error occurs.</exception>
        public void UpdatePictureMetadata(FlickrPictureMetadata metadata)
        {
            UpdateTitleAndDescription(metadata);

            UpdateTags(metadata);

            UpdateAuthor(metadata);

            UpdateGeoCoordinates(metadata);

            UpdateDateTaken(metadata);
        }
        #endregion

        #region Private methods
        /// <summary>
        /// Updates the autor if it has changed.
        /// </summary>
        /// <param name="metadata">The metadata for the Flickr picture.</param>
        private static void UpdateAuthor(FlickrPictureMetadata metadata)
        {
            if (metadata.AuthorChanged)
            {
                Trace.TraceInformation("Unable to update author for Flickr picture with id {0} from '{1}' to '{2}'.",
                   metadata.Id, metadata.AuthorOriginal, metadata.Author);
            }
        }

        /// <summary>
        /// Updates the geo-coordinates if they have changed.
        /// </summary>
        /// <param name="metadata">The metadata for the Flickr picture.</param>
        private void UpdateGeoCoordinates(FlickrPictureMetadata metadata)
        {
            if (metadata.GpsLatitudeChanged || metadata.GpsLongitudeChanged)
            {
                if (metadata.GpsLatitude.HasValue && metadata.GpsLongitude.HasValue)
                {
                    if (!SimulateUpdatingMetadata)
                    {
                        _flickrApi.PhotosGeoSetLocation(metadata.FlickrId, metadata.GpsLatitude.Value, metadata.GpsLongitude.Value);
                    }

                    Trace.TraceInformation("Updated geolocation for Flickr picture with id {0} from ({1} , {2}) to ({3} , {4}).",
                       metadata.Id, metadata.GpsLatitudeOriginal.ToString(), metadata.GpsLongitudeOriginal.ToString(), metadata.GpsLatitude.Value, metadata.GpsLongitude.Value);
                }
                else
                {
                    if (!SimulateUpdatingMetadata)
                    {
                        _flickrApi.PhotosGeoRemoveLocation(metadata.FlickrId);
                    }

                    Trace.TraceInformation("Removed geolocation for Flickr picture with id {0}.",
                       metadata.Id);
                }
            }
        }

        /// <summary>
        /// Updates the date taken if it has changed.
        /// </summary>
        /// <param name="metadata">The metadata for the Flickr picture.</param>
        private void UpdateDateTaken(FlickrPictureMetadata metadata)
        {
            if (metadata.DateTakenChanged)
            {
                if (metadata.DateTaken <= _MinimumSupportedFlickrDate)
                {
                    Trace.TraceWarning("Ignoring new date taken for Flickr picture with id {0} because it is too early.\nDateTaken '{1}'.",
                        metadata.Id, metadata.DateTaken);
                }
                else
                {

                    if (!SimulateUpdatingMetadata)
                    {
                        _flickrApi.PhotosSetDates(metadata.FlickrId, metadata.DateTaken, DateGranularity.FullDate);
                    }

                    Trace.TraceInformation("Updated date taken for Flickr picture with id {0}.\nDateTaken: from '{1}' to '{2}'.",
                        metadata.Id, metadata.DateTakenOriginal, metadata.DateTaken);
                }
            }
        }        

        /// <summary>
        /// Updates the title and description if they have changed.
        /// </summary>
        /// <param name="metadata">The metadata for the Flickr picture.</param>
        private void UpdateTitleAndDescription(FlickrPictureMetadata metadata)
        {
            if (metadata.TitleChanged || metadata.DescriptionChanged)
            {
                if (!SimulateUpdatingMetadata)
                {
                    _flickrApi.PhotosSetMeta(metadata.Id, metadata.Title, metadata.Description);
                }

                if (metadata.TitleChanged)
                {
                    Trace.TraceInformation("Updated title for Flickr picture with id {0} from '{1}' to '{2}'.",
                       metadata.Id, metadata.TitleOriginal, metadata.Title);
                }

                if (metadata.DescriptionChanged)
                {
                    Trace.TraceInformation("Updated description for Flickr picture with id {0} from '{1}' to '{2}'.",
                       metadata.Id, metadata.DescriptionOriginal, metadata.Description);
                }
            }
        }

        /// <summary>
        /// Updates the tags if they have changed.
        /// </summary>
        /// <param name="metadata">The metadata for the Flickr picture.</param>
        private void UpdateTags(FlickrPictureMetadata metadata)
        {
            if (metadata.TagsChanged)
            {
                if (!SimulateUpdatingMetadata)
                {
                    _flickrApi.PhotosSetTags(metadata.FlickrId, metadata.Tags);
                }

                Trace.TraceInformation("Updated tags for Flickr picture with id {0} from '{1}' to '{2}'.",
                   metadata.Id, String.Join(";", metadata.TagsOriginal), String.Join(";", metadata.Tags));
            }
        }
        #endregion
    }
}
